﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Win32API
{
    //保存剪贴板内容,在使用后恢复 
    //.NET框架和Windows.Forms.Clipboard该类仅允许处理可序列化的数据
    //那么，如果剪贴板包含来自不兼容应用程序的数据怎么办?简单!.NET Framework认为剪贴板为空。
    //要读取和处理这些数据，我们需要调用user32.dll和kernerl32.dll。
    public class ClipTool
    {
        /// <summary>
        /// 指定数据按照指定格式放入剪切板中
        /// https://baike.baidu.com/item/SetClipboardData/3266114
        /// </summary>
        /// <param name="Format">用来指定要放到剪切板中的数据的格式</param>
        /// <param name="hData">指定具有指定格式的数据的句柄,该参数可以是空</param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static public extern IntPtr SetClipboardData(uint Format, IntPtr hData);
        /// <summary>
        /// 枚举剪贴板内数据类型
        /// </summary>
        /// <param name="format"></param>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        static extern uint EnumClipboardFormats(uint format);
        /// <summary>
        /// 打开剪贴板查看
        /// 如果另一个窗口已经打开剪贴板,函数会失败.每次成功调用后都应有CloseClipboard调用.
        /// </summary>
        /// <param name="hWndNewOwner">为NULL的话,则打开剪贴板与当前任务相关</param>
        /// <returns>如果函数执行成功,返回非零值.</returns>
        [DllImport("user32.dll", SetLastError = true)]
        private static extern int OpenClipboard(IntPtr hWndNewOwner);
        /// <summary>
        /// 打开剪贴板并获取剪贴板内容
        /// </summary>
        /// <param name="uFormat"></param>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr GetClipboardData(uint uFormat);
        /// <summary>
        /// 关闭剪贴板,这使其他窗口或程序能访问剪贴板
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        private static extern int CloseClipboard();
        /// <summary>
        /// 锁定内存块
        /// </summary>
        /// <param name="hMem">指定的内存指针</param>
        /// <returns>返回一个地址值</returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int GlobalLock(IntPtr hMem);
        /// <summary>
        /// 解锁内存块
        /// </summary>
        /// <param name="hMem">指定的内存指针</param>
        /// <returns>返回一个地址值</returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int GlobalUnlock(IntPtr hMem);
        /// <summary>
        /// 获取内存块大小
        /// </summary>
        /// <param name="hMem"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        public static extern uint GlobalSize(IntPtr hMem);

        /// <summary>
        /// 剪贴板数据保存目标数据列表
        /// </summary>
        private static List<byte[]> _bytes = new List<byte[]>();
        /// <summary>
        /// 剪贴板数据类型列表
        /// </summary>
        private static List<uint> _formats = new List<uint>();

        /// <summary>
        /// 保存剪贴板内容，返回真为成功，false为失败。
        /// </summary>
        /// <returns></returns>
        public static bool SaveClip()
        {
            bool fi = true;
            if (OpenClipboard((IntPtr)0) < 1)
            {
                return false;
            }
            else
            {
                _bytes.Clear();
                _formats.Clear();
            }
            uint uFormat = 0;
            while (fi)
            {
                uFormat = EnumClipboardFormats(uFormat);//枚举剪贴板所有数据类型
                if (uFormat > 0)
                {
                    _formats.Add(uFormat);
                    IntPtr hMem = GetClipboardData(uFormat);
                    GlobalLock(hMem);
                    if (hMem != (IntPtr)0)
                    {
                        uint size = GlobalSize(hMem);
                        IntPtr s = (IntPtr)GlobalLock(hMem);
                        byte[] buffer = new byte[(int)size];
                        if (size > 0)
                        {
                            Marshal.Copy(s, buffer, 0, (int)size);//将剪贴板数据保存到自定义字节数组
                        }
                        GlobalUnlock(hMem);
                        _bytes.Add(buffer);
                    }
                }
                else
                {
                    fi = false;
                }
            }
            CloseClipboard();
            if (_formats.Count > 0)
            { return true; }
            else { return false; }
        }

        /// <summary>
        /// 恢复保存的数据
        /// </summary>
        /// <returns></returns>
        public static bool RestoreClip()
        {
            bool fi = true;
            if (_formats.Count <= 0)
            {
                return false;
            }
            if (OpenClipboard((IntPtr)0) < 1)
            {
                return false;
            }
            if (fi)
            {
                for (int i = 0; i < _formats.Count; i++)
                {
                    int size = _bytes[i].Length;
                    IntPtr si = Marshal.AllocHGlobal(size);
                    if (size > 0)
                    {
                        Marshal.Copy(_bytes[i], 0, si, size);
                    }
                    SetClipboardData(_formats[i], si);
                }
            }
            CloseClipboard();
            if (_formats.Count > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }


    }
}